﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Drawing;
using System.Data;

namespace Shapefile
{

    public class ESRIShape
    {
        internal ShapeType type_;
        private int recordNumber_;
        private StringDictionary metaData_;
        private IDataRecord dataRecord_;

        protected internal ESRIShape(ShapeType shapeType, int recordNumber, StringDictionary metaData, IDataRecord dataRecord)
        {
            type_ = shapeType;
            metaData_ = metaData;
            recordNumber_ = recordNumber;
            dataRecord_ = dataRecord;
        }

        public string GetMetaData(string name)
        {
            if ((metaData_ != null) && (metaData_.ContainsKey(name)))
            {
                return metaData_[name];
            }
            else
            {
                return null;
            }
        }

        public string[] GetMetaDataNames()
        {
            if ((metaData_ != null) && (metaData_.Keys.Count > 0))
            {
                List<string> names = new List<string>(metaData_.Keys.Count);
                foreach (string key in metaData_.Keys)
                {
                    names.Add(key);
                }
                return names.ToArray();
            }
            else
            {
                return null;
            }
        }

        public virtual void Render(Graphics g)
        {

        }

        public IDataRecord DataRecord
        {
            get { return dataRecord_; }
        }

        public int RecordNumber
        {
            get { return recordNumber_; }
        }

        public ShapeType Type
        {
            get { return type_; }
        }

        protected RectangleD ParseBoundingBox(byte[] value, int startIndex, ByteOrder order)
        {
            return new RectangleD(BinaryConverter.ToDouble(value, startIndex, order),
                BinaryConverter.ToDouble(value, startIndex + 8, order),
                BinaryConverter.ToDouble(value, startIndex + 16, order),
                BinaryConverter.ToDouble(value, startIndex + 24, order));
        }

        protected void ParsePolyLineOrPolygon(byte[] shapeData, out RectangleD boundingBox, out List<PointD[]> parts)
        {
            boundingBox = new RectangleD();
            parts = null;

            if (shapeData == null)
            {
                throw new ArgumentNullException("ShapeData is Null!");
            }

            /* 
                PolyLine or Polygon Record Contents
                Position    Field           Value       Type        Number      Byte Order
                Byte 0      Shape Type      3           Integer     1           Little
                Byte 4      Box             Box         Double      4           Little
                Byte 36     NumParts        NumParts    Integer     1           Little
                Byte 40     NumPoints       NumPoints   Integer     1           Little
                Byte 44     Parts           Parts       Integer     NumParts    Little
                Byte X      Points          Points      Point       NumPoints   Little
                Note: X = 44 + 4 * NumParts
            */

            if (shapeData.Length < 44)
            {
                throw new InvalidOperationException("Invalid shape data");
            }

            // 提取边界盒，部件数和点的数量
            boundingBox = ParseBoundingBox(shapeData, 12, ByteOrder.Little);
            int numParts = BinaryConverter.ToInt32(shapeData, 44, ByteOrder.Little);
            int numPoints = BinaryConverter.ToInt32(shapeData, 48, ByteOrder.Little);

            if (shapeData.Length != 52 + (4 * numParts) + (16 * numPoints))
            {
                throw new InvalidOperationException("Invalid shape data");
            }

            int partsOffset = 52 + (4 * numParts);
            parts = new List<PointD[]>(numParts);

            for (int part = 0; part < numParts; part++)
            {
                // Points的第一个位置
                int startPart = (BinaryConverter.ToInt32(shapeData, 52 + (4 * part), ByteOrder.Little) * 16) + partsOffset;

                int numBytes;
                if (part == numParts - 1)
                {
                    numBytes = shapeData.Length - startPart;
                }
                else
                {
                    int nextPart = (BinaryConverter.ToInt32(shapeData, 52 + (4 * (part + 1)), ByteOrder.Little) * 16) + partsOffset;
                    numBytes = nextPart - startPart;
                }

                int numPointsInPart = numBytes / 16;

                PointD[] points = new PointD[numPointsInPart];
                for (int point = 0; point < numPointsInPart; point++)
                {
                    points[point] = new PointD(BinaryConverter.ToDouble(shapeData, startPart + (16 * point), ByteOrder.Little),
                        BinaryConverter.ToDouble(shapeData, startPart + 8 + (16 * point), ByteOrder.Little));
                }

                parts.Add(points);
            }
        }
    }
}
